package e310

import (
	"encoding/binary"
	"errors"
)

type Packet struct {
	Raw             []byte
	Type            PacketType
	Cmd             byte
	ParameterLength int
	ParameterData   []byte
	CheckSum        byte
}

func (p *Packet) Marshal() []byte {
	p.ParameterLength = len(p.ParameterData)
	payloadLen := p.ParameterLength
	packet := make([]byte, 7+payloadLen)
	packet[0] = PacketHead
	packet[1] = byte(p.Type)
	packet[2] = p.Cmd
	if payloadLen == 0 {
		packet[3] = 0x00
		packet[4] = 0x00
	} else {
		binary.BigEndian.PutUint16(packet[3:5], uint16(payloadLen))
		copyBuf(p.ParameterData, 0, packet, 5, payloadLen)
	}
	// calc checksum
	packet[7+payloadLen-2] = crc(packet[1 : 5+payloadLen])
	packet[7+payloadLen-1] = PacketTail
	p.Raw = packet
	return packet
}

func (p *Packet) Parse(packet []byte) (err error) {
	checksum := crc(packet[1 : len(packet)-2])
	compare := packet[len(packet)-2]
	if checksum != compare {
		err = errors.New("packet checksum error")
		return
	}
	if packet[0] != PacketHead {
		err = errors.New("packet header")
		return
	}
	p.Type = PacketType(packet[1])
	p.Cmd = packet[2]
	p.ParameterLength = int(binary.BigEndian.Uint16(packet[3:5]))
	if p.ParameterLength+7 > len(packet) {
		err = errors.New("packet parameter length too long")
		return
	}
	if p.ParameterLength > 0 {
		p.ParameterData = make([]byte, p.ParameterLength)
		copyBuf(packet, 5, p.ParameterData, 0, p.ParameterLength)
	}
	return
}

func GenPacket(cmd byte, pType PacketType, parameters ...byte) []byte {
	p := Packet{Cmd: cmd, Type: pType, ParameterData: parameters}
	packet := p.Marshal()
	return packet
}
